<?php

namespace s94\wechat;
use Exception;

/**
 * 分账类，合并了服务商分账和普通微信商户分账。
 * 配置中，存在 sub_mchid 表示服务商分账，否则表示普通微信商户分账
 */
class ProfitSharing extends Pay
{
    //与分账方的关系类型 常量
    const SERVICE_PROVIDER = 'SERVICE_PROVIDER'; //服务商
    const STORE = 'STORE'; //门店
    const STAFF = 'STAFF'; //员工
    const STORE_OWNER = 'STORE_OWNER'; //店主
    const PARTNER = 'PARTNER'; //合作伙伴
    const HEADQUARTER = 'HEADQUARTER'; //总部
    const BRAND = 'BRAND'; //品牌方
    const DISTRIBUTOR = 'DISTRIBUTOR'; //分销商
    const USER = 'USER'; //用户
    const SUPPLIER = 'SUPPLIER'; //供应商

    /**格式化分账接收方
     * @param string $account 账号，三种格式：1、商户号；2、用户相对服务商的openid；3、sub:用户相对子商户的openid
     * @param string|null $name 接收方姓名
     * @return array 格式化后的分账接收方对象
     * @throws Exception
     */
    private function receiver($account, $name=null)
    {
        $res = [];
        if (is_numeric($account)){
            $res['type'] = 'MERCHANT_ID';
            $res['account'] = $account;
        }else{
            preg_match("/^(sub:)?(.+)$/", $account, $ms);
            $res['type'] = $ms[1] ? 'PERSONAL_SUB_OPENID' : 'PERSONAL_OPENID';
            $res['account'] = $ms[2];
        }
        if ($res['type']=='PERSONAL_SUB_OPENID'){
            self::assert($this->config('sub_appid',''), '分账接收方为子商户openid时，配置【sub_appid】不能为空');
        }
        if ($name) $res['name'] = $this->encryptStr($name);
        return $res;
    }

    /**分账接口需要的分账接收方数据格式化
     * @param string $account 接收方账号
     * @param int $amount 分账金额(分)
     * @param string $description 分账描述
     * @param string|null $name 接收方姓名(传则校验)
     * @return array 格式化后的分账接收方对象
     * @throws Exception
     */
    private function receiverOrder($account, $amount, $description, $name=null)
    {
        $res = $this->receiver($account, $name);
        $res['amount'] = (int)$amount;
        $res['description'] = $description;
        return $res;
    }

    /**添加分账方接口需要的分账接收方数据格式化
     * @param string $account 接收方账号
     * @param string $relation_type 与分账方的关系类型，建议直接传入本类定义的常量
     * @param string|null $name 接收方姓名
     * @return array 格式化后的分账接收方对象
     * @throws Exception
     */
    private function receiverAdd($account, $relation_type, $name=null)
    {
        $res = $this->receiver($account, $name);
        $type_map = ['SERVICE_PROVIDER','STORE','STAFF','STORE_OWNER','PARTNER','HEADQUARTER','BRAND','DISTRIBUTOR','USER','SUPPLIER'];
        if (in_array($relation_type, $type_map)){
            $res['relation_type'] = $relation_type;
        }else{
            self::assert(mb_strlen($relation_type) <= 10, '自定义的分账关系最多10个字');
            $res['relation_type'] = 'CUSTOM';
            $res['custom_relation'] = $relation_type;
        }
        return $res;
    }

    /**请求分账【异步】
     * @param string $out_order_no 商户分账单号，唯一
     * @param string $transaction_id 微信支付订单号
     * @param array $list 分账接收对象列表，格式：[['account'=>接收账号(三种格式：1、商户号；2、服务商openid；3、sub:子商户openid), 'amount'=>分账金额(分), 'description'=>分账描述, 'name'=>'接收方姓名(传则校验)'],...]
     * @param mixed $is_last 是否为最后一次分账，如果为true，此支付单将不能再次分账。特别说明：如果$list为空数组，$is_last为true，表示解冻剩余资金
     * @return array
     * @throws Exception
     */
    public function order($out_order_no, $transaction_id, array $list=[], $is_last=false)
    {
        $receivers = [];
        $has_sub_openid = false;
        self::assert($is_last || count($list), '分账接收对象列表不能为空');
        foreach ($list as $row){
            self::assert(!empty($row['account']), '分账接收方账号【account】必填');
            self::assert(!empty($row['amount']), '分账金额【amount】必填');
            self::assert(!empty($row['description']), '分账描述【description】必填');
            $row = $this->receiverOrder($row['account'], $row['amount'], $row['description'], $row['name']??'');
            if ($row['type']=='PERSONAL_SUB_OPENID') $has_sub_openid = true;
            $receivers[] = $row;
        }
        $post_data = [
            'transaction_id' => $transaction_id,
            'out_order_no' => $out_order_no,
        ];
        if ($receivers){
            $post_data['appid'] = $this->config('appid');
            $post_data['receivers'] = $receivers;
            $post_data['unfreeze_unsplit'] = !!$is_last;
            $api = 'v3/profitsharing/orders';
        }else{
            $post_data['description'] = '解冻全部剩余资金';
            $api = 'v3/profitsharing/orders/unfreeze';
        }
        if ($sub_mchid = $this->config('sub_mchid','')){
            $post_data['sub_mchid'] = $sub_mchid;
        }
        if ($has_sub_openid) {
            self::assert($post_data['sub_mchid'], "配置【sub_mchid】不能为空");
            $post_data['sub_appid'] = $this->config('sub_appid');
        }
        $res = $this->apiPay($api, $post_data);
        return $res;
    }
    /**分账结果查询
     * @param string $out_order_no 商户分账单号
     * @param string $transaction_id 微信支付订单号
     * @return mixed
     */
    public function orderInfo($out_order_no, $transaction_id)
    {
        $query = [
            'transaction_id'=> $transaction_id,
        ];
        if ($sub_mchid = $this->config('sub_mchid','')){
            $query['sub_mchid'] = $sub_mchid;
        }
        $api = 'v3/profitsharing/orders/'.$out_order_no;
        $api .= '?'.http_build_query($query);
        $res = $this->apiPay($api);
        return $res;
    }

    /**分账回退【同步】，注：分账给个人的没法退
     * @param string $out_order_no 商户分账单号
     * @param string $out_return_no 商户分账回退单号
     * @param string $return_mchid 回退的商户号，对应分账接口的分账接收放商户号
     * @param mixed $amount 回退金额，单位分
     * @param string $description 回退描述
     * @return array
     */
    public function orderReturn($out_order_no, $out_return_no, $return_mchid, $amount, $description)
    {
        $post_data = [
            'out_order_no' => $out_order_no,
            'out_return_no' => $out_return_no,
            'return_mchid' => $return_mchid,
            'amount' => (int)$amount,
            'description' => $description,
        ];
        if ($sub_mchid = $this->config('sub_mchid','')){
            $post_data['sub_mchid'] = $sub_mchid;
        }
        $res = $this->apiPay('v3/profitsharing/return-orders', $post_data);
        return $res;
    }
    /**分账退回结果查询
     * @param string $out_return_no 商户分账退回单号
     * @param string $out_order_no 商户分账单号
     * @return mixed
     */
    public function orderReturnInfo($out_return_no, $out_order_no)
    {
        $query = [
            'out_order_no'=> $out_order_no,
        ];
        if ($sub_mchid = $this->config('sub_mchid','')){
            $query['sub_mchid'] = $sub_mchid;
        }
        $api = 'v3/profitsharing/return-orders/'.$out_return_no;
        $api .= '?'.http_build_query($query);
        $res = $this->apiPay($api);
        return $res;
    }
    /**解冻剩余资金
     * @param string $out_order_no 商户分账单号，唯一
     * @param string $transaction_id 微信支付订单号
     * @return array
     */
    public function unfreeze($out_order_no, $transaction_id)
    {
        return $this->order($out_order_no, $transaction_id, [], true);
    }
    /**剩余分账金额查询
     * @param $transaction_id 微信支付订单号
     * @return array
     */
    public function residueAmount($transaction_id)
    {
        $api = 'v3/profitsharing/transactions/'.$transaction_id.'/amounts';
        $res = $this->apiPay($api);
        return $res;
    }
    /**查询最大分账比例，如果是普通商户，此方法固定返回：["sub_mchid"=>"","max_ratio"=>10000]
     * @return mixed
     * @throws Exception
     */
    public function maxRatio()
    {
        if ($sub_mchid = $this->config('sub_mchid','')){
            $api = 'v3/profitsharing/merchant-configs/'.$sub_mchid;
            $res = $this->apiPay($api);
        }else{
            $res = ["sub_mchid"=>"","max_ratio"=>10000];
        }
        return $res;
    }
    //------------------------------------以下为分账方管理--------------------------------------------

    /**添加分账接收方
     * @param string $account 接收方账号，三种格式：1、商户号；2、服务商openid；3、sub:子商户openid
     * @param string $relation_type 与分账方的关系，使用对象常量，例如：ProfitSharing::BRAND；或者自定义，例如：引流服务
     * @param string $name 接收方姓名，或者商户全称。如果账号是商户号，此项必填
     * @return mixed
     * @throws Exception
     */
    public function addReceiver($account, $relation_type, $name=null)
    {
        $post_data = [
            'appid' => $this->config('appid'),
            'sub_mchid' => $this->config('sub_mchid'),
        ];
        $receiver = $this->receiverAdd($account, $relation_type, $name);
        if ($receiver['type']=='MERCHANT_ID'){
            self::assert(!empty($receiver['name']), '分账接收方为商户号时，商户全称【name】必填');
        }elseif($receiver['type']=='PERSONAL_SUB_OPENID'){
            $post_data['sub_appid'] = $this->config('sub_appid','');
            self::assert($post_data['sub_appid'], '分账接收方为子商户openid时，配置【sub_appid】不能为空');
        }
        $post_data = array_merge($post_data, $receiver);
        $res = $this->apiPay('v3/profitsharing/receivers/add', $post_data);
        if (!empty($res['name'])){
            $res['name'] = $this->decryptStr($res['name']);
        }
        return $res;
    }

    /**删除分账接收方
     * @param string $account 接收方账号，三种格式：1、商户号；2、服务商openid；3、sub:子商户openid
     * @return mixed
     */
    public function deleteReceiver($account)
    {
        $post_data = [
            'appid' => $this->config('appid'),
            'sub_mchid' => $this->config('sub_mchid'),
        ];
        $receiver = $this->receiver($account);
        if($receiver['type']=='PERSONAL_SUB_OPENID'){
            $post_data['sub_appid'] = $this->config('sub_appid','');
            self::assert($post_data['sub_appid'], '分账接收方为子商户openid时，配置【sub_appid】不能为空');
        }
        $post_data = array_merge($post_data, $receiver);
        $res = $this->apiPay('v3/profitsharing/receivers/delete', $post_data);
        return $res;
    }

}
